using System;
using System.Drawing;
using Microsoft.Win32;
using System.Runtime.InteropServices;
using System.Drawing.Drawing2D;
using System.Drawing.Imaging;
using System.Threading;

namespace Lizk.Utilities
{
    /// <summary>
    /// Contains information about how to position a wallpaper on a desktop screen.
    /// </summary>
    public struct ScreenInfo {
        /// <summary>
        /// The bounds of the screen on which to draw the wallpaper.
        /// </summary>
        public Rectangle ScreenBounds;
        /// <summary>
        /// The bounds of the area on the screen where the wallpaper is allowed to be drawn (client area).
        /// </summary>
        public Rectangle WallpaperBounds;
        /// <summary>
        /// How to align the Wallpaper in the wallpaper bounds.
        /// </summary>
        public Alignment Alignment;
        /// <summary>
        /// Whether or not to constrain the proportions of the wallpaper.
        /// </summary>
        public bool ConstrainProportions;
        /// <summary>
        /// How much the wallpaper is allowed to stretch in decimals: 0.1 is 10 percent.
        /// </summary>
        public double StretchTolerance;
        /// <summary>
        /// Which brush to use to paint the background, that the wallpaper is not covering.
        /// </summary>
        public Brush Background;
        /// <summary>
        /// Genric Constructor.
        /// </summary>
        /// <param name="ScreenBounds">The bounds of the screen on which to draw the wallpaper.</param>
        /// <param name="WallpaperBounds">The bounds of the area on the screen where the wallpaper is allowed to be drawn (client area).</param>
        /// <param name="Alignment">How to align the Wallpaper in the wallpaper bounds.</param>
        /// <param name="Background">Which brush to use to paint the background, that the wallpaper is not covering.</param>
        /// <param name="ConstrainProportions">Whether or not to constrain the proportions of the wallpaper.</param>
        /// <param name="StretchTolerance">How much the wallpaper is allowed to stretch in decimals: 0.1 is 10 percent.</param>
        public ScreenInfo(Rectangle ScreenBounds, Rectangle WallpaperBounds, Alignment Alignment, Brush Background, bool ConstrainProportions, double StretchTolerance) 
        {
            this.ScreenBounds = ScreenBounds;
            this.WallpaperBounds = WallpaperBounds;
            this.Alignment = Alignment;
            this.ConstrainProportions = ConstrainProportions;
            this.StretchTolerance = StretchTolerance;
            this.Background = Background;
        }
    }

    /// <summary>
    /// Alignment of a wallpaper set using ScreenInfo struct
    /// </summary>
    public enum Alignment
    {
        /// <summary>
        /// Top center of the bounds
        /// </summary>
        Top,
        /// <summary>
        /// Top right corner
        /// </summary>
        TopRight,
        /// <summary>
        /// Right center of the bounds
        /// </summary>
        Right,
        /// <summary>
        /// Bottom Right corner of the bounds.
        /// </summary>
        BottomRight,
        /// <summary>
        /// Bottom center of the bounds
        /// </summary>
        Bottom,
        /// <summary>
        /// Bottom Left corner of the bounds
        /// </summary>
        BottomLeft,
        /// <summary>
        /// Left center of the bounds
        /// </summary>
        Left,
        /// <summary>
        /// Top left corner of the bounds
        /// </summary>
        TopLeft,
        /// <summary>
        /// Center of the bounds.
        /// </summary>
        Center
    }


    /// <summary>
    /// Encapsulates functions for setting the desktop wallpaper.
    /// </summary>
    public class DesktopWallpaper
    {

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni);
        private const int SPI_SETDESKWALLPAPER = 20;
        private const int SPI_GETWORKAREA = 48;
        private const int SPIF_SENDCHANGE = 0x2;
        private static Mutex waitForIt = new Mutex();
        /// <summary>
        /// Wallpaper style.
        /// </summary>
        public enum Style
        {
            /// <summary>
            /// Center the image on the screen, constrain proportions
            /// </summary>
            Center,
            /// <summary>
            /// Stretch the image to fill the entire screen.
            /// </summary>
            Stretch,
            /// <summary>
            /// Duplicate the image creating a tile that fills the entire screen.
            /// </summary>
            Tile
        }

 

        /// <summary>
        /// Set a BMP file as the windows wallpaper.
        /// </summary>
        /// <param name="BMPFilename">Path to BMP file</param>
        /// <param name="Style">The style to use.</param>
        public static void SetWallpaper(string BMPFilename, Style Style)
        {
            SetRegDB(Style, BMPFilename);
            SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, BMPFilename, SPIF_SENDCHANGE);
        }

        private static void SetRegDB(Style Style, string path)
        {
            waitForIt.WaitOne();
            RegistryKey key = Registry.CurrentUser.OpenSubKey("Control Panel\\Desktop", true);
            switch (Style)
            {
                case Style.Center:
                    key.SetValue("TileWallpaper", "0");
                    key.SetValue("WallpaperStyle", "0");
                    break;
                case Style.Stretch:
                    key.SetValue("TileWallpaper", "0");
                    key.SetValue("WallpaperStyle", "2");
                    break;
                case Style.Tile:
                    key.SetValue("TileWallpaper", "1");
                    key.SetValue("WallpaperStyle", "0");
                    break;
            }
            key.SetValue("Wallpaper", path);
            key.Close();
            waitForIt.ReleaseMutex();
        }

        /// <summary>
        /// Creates a single image that is formatted so it seems that the windows desktop has different wallpapers on all screens.
        /// </summary>
        /// <param name="screens">Info about which screens there are and how to position each wallpaper on the screens.</param>
        /// <param name="images">An array of images to use as wallpapers on the screens</param>
        /// <returns>An image that if used as a tiled Wallpaper on windows will show a different image on every screen.</returns>
        public static Image CreateMultiDesktopWallpaper(ScreenInfo[] screens, Image[] images)
        {
            waitForIt.WaitOne();
            Rectangle WallpaperBounds = new Rectangle(0, 0, 0, 0);
            // Set wallpaper size according to the position and size of all screens.
            foreach (ScreenInfo dm in screens)
            {
                if (dm.ScreenBounds.X < WallpaperBounds.X)
                    WallpaperBounds.X = dm.ScreenBounds.X;

                if (dm.ScreenBounds.Y < WallpaperBounds.Y)
                    WallpaperBounds.Y = dm.ScreenBounds.Y;

                if (dm.ScreenBounds.Width + dm.ScreenBounds.X > WallpaperBounds.Width)
                    WallpaperBounds.Width = dm.ScreenBounds.Width + dm.ScreenBounds.X;

                if (dm.ScreenBounds.Height + dm.ScreenBounds.Y > WallpaperBounds.Height)
                    WallpaperBounds.Height = dm.ScreenBounds.Height + dm.ScreenBounds.Y;

            }

            // Create a bitmap with width and height to encompass all screens
            Bitmap bmPhoto = new Bitmap((-1 * WallpaperBounds.X) + WallpaperBounds.Width, (-1 * WallpaperBounds.Y) + WallpaperBounds.Height, PixelFormat.Format24bppRgb);
            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.Clear(Color.Black);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            if (WallpaperBounds.X < 0)
                WallpaperBounds.X *= -1;

            if (WallpaperBounds.Y < 0)
                WallpaperBounds.Y *= -1;

            for (int i = 0; i < screens.Length; i++)
            {
                Image image = images[i % images.Length];
                Rectangle imageDestination = new Rectangle(0, 0, 0, 0);

                double screenProportion = (double)screens[i].WallpaperBounds.Width / (double)screens[i].WallpaperBounds.Height;
                double imageProportion = (double)image.Width / (double)image.Height;
                double proportionDifference = screenProportion - imageProportion;

                if (proportionDifference < 0)
                    proportionDifference *= -1;

                if (screenProportion > imageProportion && proportionDifference > screens[i].StretchTolerance && screens[i].ConstrainProportions)
                {
                    imageDestination.Height = screens[i].WallpaperBounds.Height;
                    imageDestination.Width = (int)(((double)screens[i].WallpaperBounds.Height / (double)image.Height) * (double)image.Width);
                    //                        image = (screens[i].Width - destWidth) / 2;
                }
                else if (screenProportion < imageProportion && proportionDifference > screens[i].StretchTolerance && screens[i].ConstrainProportions)
                {
                    imageDestination.Width = screens[i].WallpaperBounds.Width;
                    imageDestination.Height = (int)(((double)screens[i].WallpaperBounds.Width / (double)image.Width) * (double)image.Height);
                    //                        destY = (screens[i].Height - destHeight) / 2;
                }
                else
                {
                    imageDestination.Width = screens[i].WallpaperBounds.Width;
                    imageDestination.Height = screens[i].WallpaperBounds.Height;
                }
                imageDestination.Y = screens[i].WallpaperBounds.Y;
                imageDestination.X = screens[i].WallpaperBounds.X;
                if (screens[i].Alignment == Alignment.BottomRight || screens[i].Alignment == Alignment.BottomLeft || screens[i].Alignment == Alignment.Bottom)
                    imageDestination.Y += screens[i].WallpaperBounds.Height - imageDestination.Height;

                if (screens[i].Alignment == Alignment.Center || screens[i].Alignment == Alignment.Top || screens[i].Alignment == Alignment.Bottom)
                    imageDestination.X += (screens[i].WallpaperBounds.Width - imageDestination.Width) / 2;

                if (screens[i].Alignment == Alignment.BottomRight || screens[i].Alignment == Alignment.TopRight || screens[i].Alignment == Alignment.Right)
                    imageDestination.X += screens[i].WallpaperBounds.Width - imageDestination.Width;

                if (screens[i].Alignment == Alignment.Left || screens[i].Alignment == Alignment.Center || screens[i].Alignment == Alignment.Right)
                    imageDestination.Y += (screens[i].WallpaperBounds.Height - imageDestination.Height) / 2;

                grPhoto.FillRectangle(screens[i].Background, new Rectangle(WallpaperBounds.X + screens[i].ScreenBounds.X, WallpaperBounds.Y + screens[i].ScreenBounds.Y, screens[i].ScreenBounds.Width, screens[i].ScreenBounds.Height));

                grPhoto.DrawImage(image,
                   new Rectangle(WallpaperBounds.X + screens[i].ScreenBounds.X + imageDestination.X, WallpaperBounds.Y + screens[i].ScreenBounds.Y + imageDestination.Y, imageDestination.Width, imageDestination.Height),
                   new Rectangle(0, 0, image.Width, image.Height), GraphicsUnit.Pixel);
                image.Dispose();
            }
            grPhoto.Dispose();
            waitForIt.ReleaseMutex();
            return bmPhoto;
        }
        /// <summary>
        /// Creates a desktop wallpaper that shows different images on every screen.
        /// </summary>
        /// <param name="screens">Bounds of the screens on the target machine</param>
        /// <param name="wallpapers">Paths to the files to use as wallpaper images</param>
        /// <param name="constrainProportions">Whether or not to constrain the proportions of the images</param>
        /// <param name="strechTolerance">How much the images are allowed to be stretched when constraining proportions in order to fill entire screen. In decimals: 0.1 is 10 percent.</param>
        /// <returns>An image containing the combined desktop wallpaper</returns>
        
        public static Image CreateMultiDesktopWallpaper(Rectangle[] screens, string[] wallpapers, bool constrainProportions, double strechTolerance)
        {
            waitForIt.WaitOne();
            int x = 0, y = 0, w = 0, h = 0;
            foreach (Rectangle dm in screens)
            {
                if (dm.X < x)
                    x = dm.X;

                if (dm.Y < y)
                    y = dm.Y;

                if (dm.Width + dm.X > w)
                    w = dm.Width + dm.X;

                if (dm.Height + dm.Y > h)
                    h = dm.Height + dm.Y;

            }


            Bitmap bmPhoto = new Bitmap((-1 * x) + w, (-1 * y) + h, PixelFormat.Format24bppRgb);
            Graphics grPhoto = Graphics.FromImage(bmPhoto);
            grPhoto.Clear(Color.Black);
            grPhoto.InterpolationMode = InterpolationMode.HighQualityBicubic;

            if (x < 0)
            {
                x = -1 * x;
            }
            if (y < 0)
            {
                y = -1 * y;
            }
            for (int i = 0; i < screens.Length; i++)
            {
                Image img = Image.FromFile(wallpapers[i % wallpapers.Length]);
                int destHeight = 0, destWidth = 0, destX = 0, destY = 0;

                double sProportion = (double)screens[i].Width / (double)screens[i].Height;
                double iProportion = (double)img.Width / (double)img.Height;
                double pDiff = sProportion - iProportion;
                if (pDiff < 0) pDiff = -1 * pDiff;


                if (sProportion > iProportion && pDiff > strechTolerance && constrainProportions)
                {
                    destHeight = screens[i].Height;
                    destWidth = (int)(((double)screens[i].Height / (double)img.Height) * (double)img.Width);
                    destX = (screens[i].Width - destWidth) / 2;
                }
                else if (sProportion < iProportion && pDiff > strechTolerance && constrainProportions)
                {
                    destWidth = screens[i].Width;
                    destHeight = (int)(((double)screens[i].Width / (double)img.Width) * (double)img.Height);
                    destY = (screens[i].Height - destHeight) / 2;
                }
                else
                {
                    destWidth = screens[i].Width;
                    destHeight = screens[i].Height;
                }

                grPhoto.DrawImage(img,
                   new Rectangle(x + screens[i].X + destX, y + screens[i].Y + destY, destWidth, destHeight),
                   new Rectangle(0, 0, img.Width, img.Height), GraphicsUnit.Pixel);
                img.Dispose();
            }
            grPhoto.Dispose();
            waitForIt.ReleaseMutex();
            return bmPhoto;
        }
    }



}
